package factory.one.command;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

/**
 * @author sk.z
 * 2023/7/4
 * 命令行
 **/
//@Service
public class Command implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(Command.class);

    @Autowired
    private ApplicationContext applicationContext;

    private static final Map<String, Object> beanMap = new HashMap<>();
    private static final Map<String, Method> methodMap = new HashMap<>();

    @Override
    public void run(String... args) throws Exception {
        Package pkg = this.getClass().getPackage();
        List<Class<?>> classList = SelfClassUtil.getClassList(pkg.getName(), false, null);
        for (Class clz : classList) {
            if (clz.getName().contains("Command")) {
                continue;
            }

            Object bean = null;
            try {
                bean = applicationContext.getBean(clz);
            } catch (NoSuchBeanDefinitionException ignored) {
            }
            if (null == bean) {
                continue;
            }

            String beanName = shortName(clz.getName());
            Method[] methods = clz.getMethods();
            for (Method m : methods) {
                if (m.getModifiers() != Modifier.PUBLIC) {
                    continue;
                }
                String methodName = m.getName();
                if ("equals".equals(methodName) || "toString".equals(methodName)) {
                    continue;
                }
                String fullName = beanName + "." + methodName;
                if (methodMap.containsKey(fullName)) {
                    throw new InstantiationError("method " + fullName + " is already exists!");
                }
                methodMap.put(fullName, m);
                beanMap.put(fullName, bean);
                logger.info(fullName);
            }
        }

        new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.print("cmd>");
            String[] cmd;
            while (scanner.hasNextLine()) {
                try {
                    cmd = scanner.nextLine().split(" ");
                    if (methodMap.containsKey(cmd[0])) {
                        Method method = methodMap.get(cmd[0]);
                        int count = method.getParameterCount();
                        Object[] params = new Object[count];
                        for (int i = 0; i < count; i++) {
                            params[i] = cmd[i + 1];
                        }
                        method.invoke(beanMap.get(cmd[0]), params);
                    } else {
                        System.out.println("\t unknown command [" + cmd[0] + "]");
                    }
                    System.out.print("cmd>");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            scanner.close();
        }).start();
    }

    private String shortName(String className) {
        StringBuilder sb = new StringBuilder();
        for (char c : className.toCharArray()) {
            if (Character.isUpperCase(c)) {
                sb.append(Character.toLowerCase(c));
            }
        }
        return sb.toString();
    }

}
